package pers.kw.config.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;


/**
 * Security配置
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private static final Logger logger = LoggerFactory.getLogger(SecurityConfig.class);

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder builder) throws Exception {
        builder.userDetailsService(userDetailsService());
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    @Override
    protected UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager userDetailsManager = new InMemoryUserDetailsManager();
        userDetailsManager.createUser(User.withUsername("admin").password(passwordEncoder().encode(
                "admin")).roles("ADMIN").build());
        userDetailsManager.createUser(User.withUsername("kw").password(passwordEncoder().encode(
                "kw")).roles("ROOT").build());
        return userDetailsManager;
    }

    //自定义 filter 交给工厂管理
    @Bean
    public LoginFilter loginFilter() throws Exception {
        LoginFilter loginFilter = new LoginFilter();
        loginFilter.setFilterProcessesUrl("/user/login");//指定认证 url
        loginFilter.setUsernameParameter("username");//指定接收json 用户名 key
        loginFilter.setPasswordParameter("password");//指定接收 json 密码 key
        loginFilter.setAuthenticationManager(authenticationManagerBean());
        //认证成功处理
        loginFilter.setAuthenticationSuccessHandler((req, resp, authentication) -> {
            Map<String, Object> result = new HashMap<>();
            result.put("msg", "登录成功");
            result.put("用户信息", authentication.getPrincipal());
            resp.setContentType("application/json;charset=UTF-8");
            resp.setStatus(HttpStatus.OK.value());
            String s = new ObjectMapper().writeValueAsString(result);
            resp.getWriter().println(s);
        });
        //认证失败处理
        loginFilter.setAuthenticationFailureHandler((req, resp, ex) -> {
            Map<String, Object> result = new HashMap<>();
            result.put("msg", "登录失败: " + ex.getMessage());
            resp.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            resp.setContentType("application/json;charset=UTF-8");
            String s = new ObjectMapper().writeValueAsString(result);
            resp.getWriter().println(s);
        });
        return loginFilter;
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //开启授权请求管理
        http.authorizeRequests()
                .mvcMatchers("/login.html").permitAll()
                .mvcMatchers("/favicon.ico").permitAll()
                .anyRequest().authenticated();

        //自定义登录
        http.formLogin()
                .loginPage("/login.html")
        ;
        //todo 前后端分离登录自定义认证处理
        http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);

        //自定义登出
        http.logout()
                .logoutUrl("/user/logout")
                .logoutSuccessHandler((request, response,
                                       authentication) -> {
                    Map<String, Object> result = new HashMap<>();
                    result.put("msg", "注销成功");
                    result.put("用户信息", authentication.getPrincipal());
                    response.setContentType("application/json;charset=UTF-8");
                    response.setStatus(HttpStatus.OK.value());
                    String s = new ObjectMapper().writeValueAsString(result);
                    response.getWriter().println(s);
                })
                //todo 作用是什么
                //.addLogoutHandler(null)
                .clearAuthentication(true);
        ;

        //认证、授权异常响应信息处理
        //todo 加上这个 授权码模式-未登录情况访问授权url，前端只会显示异常信息，无法跳转到登录页面
        //todo 前后端分离情况，前端授权逻辑需要改造
/*        http.exceptionHandling()
                .authenticationEntryPoint((httpServletRequest, httpServletResponse,
                                           authenticationException) -> {
                    //日志输出请求信息
                    logger.error("认证失败：",authenticationException);
                    //响应消息处理
                    httpServletResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
                    httpServletResponse.setStatus(HttpStatus.UNAUTHORIZED.value());
                    httpServletResponse.getWriter().write(authenticationException.getMessage());
                })
                .accessDeniedHandler((httpServletRequest, httpServletResponse, accessDeniedException) -> {

                    logger.error("授权信息失败：",accessDeniedException);

                    httpServletResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
                    httpServletResponse.setStatus(HttpStatus.FORBIDDEN.value());
                    httpServletResponse.getWriter().write(accessDeniedException.getMessage());
                });*/

        //关闭跨站保护，否则即permitAll()
        http.csrf().disable();
    }
}
